/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.dess.base.tools;

import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * @project-name:dess
 * @package-name:com.hyts.dess.base.tools
 * @author:LiBo/Alex
 * @create-date:2020-08-06 10:03
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */
public class TemplateUtils {


    public static class SimpleTemplateHandler{


        public static final String DEFAULT_SPLIT = "@";
        /**
         * Q比重值
         */
        public static final String DEFAULT_EPL_TOKEN_PREFIX = "\\Q";
        // E
        public static final String DEFAULT_EPL_TOKEN_SUBFIX = "\\\\E";

        /**
         * 获取参数名称
         * @return
         */
        public static Set<String> getParamNames(String str,String split){
            //判断是否为空
            if(StringUtils.isBlank(str)){
               throw new IllegalArgumentException("获取相关得str字符串参数不允许为空!");
            }
            // 初始化分隔符操作
            split = StringUtils.defaultIfEmpty(split,DEFAULT_SPLIT);
            Set<String> paramNames = new HashSet<>();
            int start,end = 0;
            while(str.length()>end){
                // 截取第一个分隔符然后跳过当前得分隔符-包前不包后
                start = str.indexOf(split, end) + split.length();
                // 如果不存在则直接跳出循环,避免死循环
                if (start == -1) {
                    break;
                }
                // 包前不包后,不需要+1 ， 截取之后得最新得分割符得位置
                end = str.indexOf(split, start);
                if (end == -1) {
                    // 如果不存在则直接跳出循环,避免死循环
                    break;
                }
                paramNames.add(str.substring(start, end));
                // 进行下一个循环拼接操作得位置定位。
                end = end + split.length();
            }
            return paramNames;
        }
        /**
         使用context中对应的值替换templet中用split包围的变量名(也是context的key)
         * @param templet 模板
         * @param split 用于标识变量名的标志
         * @param context 用于替换模板中的变量
         * @return 例如  参数 : dddd$$aaa$$$$bbb$$ccc$$, $$, {<aaa, value1>, <bbb, value2>}  结果:ddddvalue1value2ccc$$
         */
        public static String transfer(String templet, String split, Map<String, String> context){
            Set<String> paramNames = getParamNames(templet, split);
            // 参数名称列表信息对象数据
            for(String paramName:paramNames){
                String value = StringUtils.defaultIfBlank(context.get(paramName),"");
                String regex = split + paramName + split ;
                templet = templet.replaceAll(regex, value);
            }
            return templet;
        }

        /**
         * 使用context中对应的值替换templet中用$$包围的变量名(也是context的key)
         * @param templet 模板
         * @param context 用于替换模板中的变量
         * @return 例如  参数 : dddd$$aaa$$$$bbb$$ccc$$, $$, {<aaa, value1>, <bbb, value2>}  结果:ddddvalue1value2ccc$$
         */
        public static String transfer(String templet, Map<String, String> context) {
            return transfer(templet, DEFAULT_SPLIT, context);
        }

        /**
         * main方法启动
         * @param args
         */
        public static void main(String[] args){
            Set<String> paramNames = getParamNames("dddd@aaa@@bbb@ccc@", "@");
            System.out.println(paramNames);
            Map<String, String> context = new HashMap<String, String>();
            context.put("aaa", "value1");
            context.put("bbb", "value2");
            String render = transfer("dddd@aaa@@bbb@ccc@", context );
            System.out.println(render);
        }
    }

    /**
     * 定制化模板处理器操作功能实现
     */
    public static class CustomTemplateHandler {

        public static final String DEFAULT_SPLIT_0 = "$";
        /**
         * 默认分割符01
         */
        public static final String DEFAULT_SPLIT_1 = "${";

        /**
         * 默认分隔符02
         */
        public static final String DEFAULT_SPLIT_2 = "#{";

        /**
         * 默认分隔符03
         */
        public static final String DEFAULT_SPLIT_3 = "}";

        /**
         * 表达式操作
         */
        abstract static class Exp{

            /**
             * 解析操作
             * @param context
             * @return
             */
            abstract String resolve(Map<String, Object> context);

            /**
             * 构建操作服务器
             * @param exp
             * @return
             */
            static Exp of(String exp){
                Preconditions.checkArgument(StringUtils.isNotBlank(exp),"传入的表达式参数不允许为空!");
                if(exp.startsWith(DEFAULT_SPLIT_1) || exp.startsWith(DEFAULT_SPLIT_2)){
                    return new VarExp(exp);
                }
                return new StrExp(exp);
            }
        }

        /**
         * 字符串表达式操作控制单位单元
         */
        static class StrExp extends Exp{
            /**
             * 表达式操作处理
             */
            String value;
            /**
             * @param exp
             */
            StrExp(String exp) {
                this.value = exp;
            }
            @Override
            String resolve(Map<String, Object> context) {
                return this.value;
            }
            @Override
            public String toString() {
                return "StrExp [value=" + value + "]";
            }
        }

        static class VarExp extends Exp{

            private String varName;

            private String defaultValue;

            Boolean nullable = false;

            /**
             * 构造器操作
             * @param varName
             * @param defaultValue
             * @param nullable
             */
            VarExp(String varName, String defaultValue, Boolean nullable) {
                this.varName = varName;
                this.defaultValue = defaultValue;
                this.nullable = nullable;
            }

            /**
             * 正则表达式信息操作
             * @param exp
             */
            VarExp(String exp){
                Preconditions.checkArgument(StringUtils.isNotBlank(exp),"传入的表达式参数不允许为空!");
                Preconditions.checkArgument(!(exp.startsWith(DEFAULT_SPLIT_1) || exp.startsWith(DEFAULT_SPLIT_2)) || !exp.endsWith(DEFAULT_SPLIT_3),
                        "表达式[" + exp + "]必须类似于#{}或${}");
                String[] nodes = exp.substring(2, exp.length() - 1).split(",");
                Preconditions.checkArgument(nodes.length > 2,"表达式[\" + exp + \"]只能出现一个','");
                this.varName = nodes[0].trim();
                this.defaultValue = nodes.length == 2 ? nodes[1].trim() : "";
                this.nullable = exp.startsWith(DEFAULT_SPLIT_0);
            }

            /**
             * 解析方法操作功能实现
             * @param context
             * @return
             */
            @Override
            String resolve(Map<String, Object> context) {
                Object value = context.get(varName);
                if (value == null && nullable) {
                    value = defaultValue == null ? "" : defaultValue;
                }
                Preconditions.checkNotNull(value,"上下文中没有指定的变量:var=" + varName + " map=" + context);
                return value.toString();
            }

            /**
             * toString 参数方法功能实现
             * @return
             */
            @Override
            public String toString() {
                return "VarExp [varName=" + varName + ", defaultValue=" + defaultValue + ", nullable="
                        + nullable + "]";
            }
        }

        /**
         * 模板
         */
        static class Template {
            List<Exp> exps = new ArrayList<>();
            static Template of(String templStr) {
                Objects.requireNonNull(templStr, "模板为空");
                Template templ = new Template();
                StringBuilder sb = new StringBuilder();
                char[] chars = templStr.toCharArray();
                for (int i = 0; i < chars.length; i++) {
                    switch (chars[i]) {
                        case '#':
                        case '$':
                            if(i < chars.length - 1 && chars[i+1] == '{'){
                                templ.addExp(Exp.of(sb.toString()));
                                sb.setLength(0);
                            }
                            sb.append(chars[i]);
                            break;
                        case '}':
                            sb.append('}');
                            if(sb.length() > 1 && sb.charAt(1) == '{'){
                                templ.addExp(Exp.of(sb.toString()));
                                sb.setLength(0);
                            }
                            break;
                        default:
                            sb.append(chars[i]);
                            break;
                    }
                }
                // 长度
                if(sb.length() > 0){
                    templ.addExp(Exp.of(sb.toString()));
                }

                return templ;
            }

            Template addExp(Exp exp) {
                Objects.requireNonNull(exp, "表达式为空");
                exps.add(exp);
                return this;
            }

            String render(Map<String, Object> context) {
                StringBuilder sb = new StringBuilder(128);
                for (Exp exp : exps) {
                    sb.append(exp.resolve(context));
                }
                return sb.toString();
            }
        }
    }

//    public static void main(String[] args) {
//        String t1 = "你好 $$name$$, 您的验证码是:$$code$$";
//        String t2 = "你好 #{name}, 您的验证码是:${code}";
//
//        Map<String, Object> param = MapBuilder.makeO("name", "haogrgr").build("code", "1314");
//        for (int i = 0; i < 100; i++) {
//            SimpleTempletUtil.render(t1, param);
//            Templ.of(t2).render(param);
//        }
//
//        String result = "";
//        long start = 0;
//
//        start = System.currentTimeMillis();
//        for (int i = 0; i < 100000; i++) {
//            param.put("code", i);
//            result = SimpleTempletUtil.render(t1, param);
//        }
//        System.out.println(System.currentTimeMillis() - start);
//        System.out.println(result);
//
//        start = System.currentTimeMillis();
//        Templ templ = Templ.of(t2);
//        for (int i = 0; i < 100000; i++) {
//            param.put("code", i);
//            result = templ.render(param);
//        }
//        System.out.println(System.currentTimeMillis() - start);
//        System.out.println(result);
//
//    }
}
